package com.universal;

import com.universal.common.utils.ResourceUtils;
import com.universal.common.utils.SpringContextHolder;
import com.universal.exception.GlobalBusinessException;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * RedisTemplateGroups
 *
 * @author: austin
 * @since: 2023/2/2 10:29
 */
public class RedisTemplateGroups {

    private static final String DEFAULT_CACHE_NAME = "default";

    public static class TemplatePair {
        RedisTemplate<String, Object> redisTemplate;
        StringRedisTemplate stringRedisTemplate;
    }

    public static Map<String, TemplatePair> groupTemplateMap = new HashMap<>();


    public static List<String> getRedisTemplateGroupNames() {
        return new ArrayList<>(groupTemplateMap.keySet());
    }

    public static StringRedisTemplate getDefaultStringRedisTemplate() {
        return getStringRedisTemplate(DEFAULT_CACHE_NAME);
    }

    private static StringRedisTemplate getStringRedisTemplate(String groupName) {
        TemplatePair pair = groupTemplateMap.get(groupName);
        if (pair != null) {
            return pair.stringRedisTemplate;
        }
        if (groupTemplateMap.containsKey(groupName)) {
            throw new GlobalBusinessException("redis template for [" + groupName + "] not found!");
        } else {
            initRedisTemplateGroup(groupName);
        }
        return getStringRedisTemplate(groupName);
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private static synchronized void initRedisTemplateGroup(String groupName) {
        if (groupTemplateMap.containsKey(groupName)) {
            return;
        }
        RedisTemplate<String, Object> redisTemplate = null;
        StringRedisTemplate stringRedisTemplate = null;
        if (DEFAULT_CACHE_NAME.equals(groupName)) {
            Map<String, RedisOperations> instanceMap = SpringContextHolder.getBeansOfType(RedisOperations.class);
            Collection<RedisOperations> instances = instanceMap.values();
            for (RedisOperations instance : instances) {
                if (instance instanceof StringRedisTemplate) {
                    stringRedisTemplate = (StringRedisTemplate) instance;
                } else {
                    redisTemplate = (RedisTemplate<String, Object>) instances;
                }
            }
        } else {
            if (!ResourceUtils.containsAnyProperty(groupName + ".redis.host", groupName + ".redis.sentinel.nodes", groupName + ".redis.cluster.nodes")) {
                groupTemplateMap.put(groupName, null);
                return;
            }
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxTotal(ResourceUtils.getInt(groupName + ".redis.jedis.pool.max-active", 10));
            poolConfig.setMinIdle(ResourceUtils.getInt(groupName + ".redis.jedis.pool.min-idle", 1));
            poolConfig.setMaxWait(Duration.ofMillis(ResourceUtils.getLong(groupName + ".redis.jedis.pool.max-wait", 30000)));

            JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(poolConfig);
            String host = ResourceUtils.getAndValidateProperty(groupName + ".redis.host");
            int port = ResourceUtils.getInt(groupName + ".redis.port", 6379);
            int database = ResourceUtils.getInt(groupName + ".redis.database", 0);
            String password = ResourceUtils.getProperty(groupName + ".redis.password");
            redisConnectionFactory.setHostName(host);
            redisConnectionFactory.setPort(port);
            redisConnectionFactory.setPassword(password);
            redisConnectionFactory.setDatabase(database);

            redisConnectionFactory.afterPropertiesSet();

            redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());

            redisTemplate.afterPropertiesSet();

            stringRedisTemplate = new StringRedisTemplate();
            stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
            stringRedisTemplate.setConnectionFactory(redisConnectionFactory);

            stringRedisTemplate.afterPropertiesSet();
        }

        if (redisTemplate == null) {
            return;
        }
        TemplatePair pair = new TemplatePair();
        pair.redisTemplate = redisTemplate;
        pair.stringRedisTemplate = stringRedisTemplate;
        groupTemplateMap.put(groupName, pair);
    }
}
